#include <time.h>
#include <process.h>
#include "ClockManager.h"

// Constructor
ClockManager::ClockManager() {
	// initialize
	head = NULL;
	mHandle = NULL;

	// start the monitor thread which watches for countdowns to finish
	monitorStart();
}

// Deconstructor
ClockManager::~ClockManager() {
	// stop the monitor thread
	monitorStop();
	
	// free up link list
	Clock *slider = head;
	while(slider) {
		slider = slider->next;
		delete[] head->name;
		delete head;
		head = slider;
	}
}


// monitor thread, watches for countdowns to finish and triggers the provided mutex
unsigned __stdcall mThread(void *_param) {
	ClockManager *cm = (ClockManager*)_param;

	// loop forever unless signaled to stop
	while(!cm->mStop) {
		Clock *slider = cm->head;

		while(slider) {
			if(slider->isCountdown) {
				// Was the finish already noted?
				if(slider->sCurr > 0) {
					// Not yet, so is it finished now?
					if(time(NULL) - slider->tStart > slider->sCurr) {
						// count down has completed
						slider->sCurr = 0;

						// Set the trigger event to signaled
						if(slider->trigger) {
							SetEvent(slider->trigger);
						}
					}
				}
			}

			slider = slider->next;
		}

		// sleep a bit to avoid max cpu
		Sleep(200);
	}

	// end the thread
	_endthreadex(0);

	return 0;
}

// start the monitor thread which watches for countdowns to finish
void ClockManager::monitorStart() {
	// avoid the possibility of starting more than one thread
	if(mHandle == NULL) {
		mStop = false;
		mHandle = (HANDLE)_beginthreadex(NULL, 0, &mThread, this, 0, &(mId));
	}
}

// stop the monitor thread which watches for countdowns to finish
void ClockManager::monitorStop() {
	DWORD lpExitCode;

	// set value which stops the monitor thread
	mStop = true;
		
	// wait for thread to finish, if it hasn't already
	GetExitCodeThread((HANDLE)mHandle, &lpExitCode);
	while(lpExitCode == STILL_ACTIVE) {
		WaitForSingleObject((HANDLE)mHandle, 200);
		GetExitCodeThread((HANDLE)mHandle, &lpExitCode);
	}

	// free up the resources
	CloseHandle(mHandle);

	// reset values
	mHandle = NULL;
}


// add a new stopwatch instance
bool ClockManager::addStopwatch(char *name) {
	// stop the monitor thread
	monitorStop();

	Clock *item = new Clock;
	item->next = item->prev = NULL;
	item->isStopwatch = true;
	item->isCountdown = false;

	// set item values
	item->name = new char[strlen(name) + 1];
	strcpy(item->name, name);

	// attach to link list of items
	if(!head) {
		head = item;
	}
	else {
		head->prev = item;
		item->next = head;
		head = item;
	}

	// start the monitor thread which watches for countdowns to finish
	monitorStart();
	
	return true;
}

// add a new countdown instance
bool ClockManager::addCountdown(char *name, int seconds, HANDLE mutex) {
	// stop the monitor thread
	monitorStop();

	Clock *item = new Clock;
	item->next = item->prev = NULL;
	item->isStopwatch = false;
	item->isCountdown = true;

	// set item values
	item->name = new char[strlen(name) + 1];
	strcpy(item->name, name);

	item->sStart = item->sCurr = seconds;

	if(mutex) {
		item->trigger = mutex;
	}
	else {
		item->trigger = NULL;
	}

	// attach to link list of items
	if(!head) {
		head = item;
	}
	else {
		head->prev = item;
		item->next = head;
		head = item;
	}

	// start the monitor thread which watches for countdowns to finish
	monitorStart();

	return true;
}


// del a stopwatch
bool ClockManager::delStopwatch(char *name) {
	// stop the monitor thread
	monitorStop();

	Clock *slider = head;

	while(slider) {
		if(slider->isStopwatch) {
		if(!strcmp(slider->name, name)) {
			if(slider->prev) {
				slider->prev->next = slider->next;
			}
			if(slider->next) {
				slider->next->prev = slider->prev;
			}
			if(slider == head) {
				head = slider->next;
			}

			delete[] slider->name;
			delete slider;

			// start the monitor thread which watches for countdowns to finish
			monitorStart();

			return true;
		}
		}
		slider = slider->next;
	}

	// start the monitor thread which watches for countdowns to finish
	monitorStart();

	return false;
}

// del a countdown
bool ClockManager::delCountdown(char *name) {
	// stop the monitor thread
	monitorStop();

	Clock *slider = head;

	while(slider) {
		if(slider->isCountdown) {
		if(!strcmp(slider->name, name)) {
			if(slider->prev) {
				slider->prev->next = slider->next;
			}
			if(slider->next) {
				slider->next->prev = slider->prev;
			}
			if(slider == head) {
				head = slider->next;
			}

			delete[] slider->name;
			delete slider;

			// start the monitor thread which watches for countdowns to finish
			monitorStart();

			return true;
		}
		}
		slider = slider->next;
	}

	// start the monitor thread which watches for countdowns to finish
	monitorStart();

	return false;
}


// stopwatch = record start time
// countdown = start/continue countdown
bool ClockManager::start(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			slider->tStart = time(NULL);
			slider->tStop = 0;

			return true;	// name found, we can exit search loop
		}

		slider = slider->next;
	}

	return false;
}

// stopwatch = record stop time
// countdown = pause countdown
bool ClockManager::stop(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			slider->tStop = time(NULL);

			if(slider->isCountdown) {
				// pause the countdown, remember # of seconds left
				slider->sCurr -= slider->tStop - slider->tStart;
			}

			return true;	// name found, we can exit search loop
		}

		slider = slider->next;
	}

	return false;
}

// stopwatch = nothing
// countdown = reset back to start
bool ClockManager::reset(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			if(slider->isCountdown) {
				// reset the countdown back to the starting number of seconds
				slider->sCurr = slider->sStart;

				return true;	// name found, we can exit search loop
			}
		}

		slider = slider->next;
	}

	return false;
}

// stopwatch = nothing
// countdown = whether countdown is complete
bool ClockManager::isComplete(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			if(slider->isCountdown) {
				// Was the finish already noted?
				if(slider->sCurr <= 0) { 
					return true;
				}
				else {
					// Not yet, so is it finished now?
					if(time(NULL) - slider->tStart > slider->sCurr) {
						// count down has completed
						slider->sCurr = 0;

						// Set the trigger event to signaled
//						if(slider->trigger) {
//							SetEvent(slider->trigger);
//						}
					}
				}
			}
		}

		slider = slider->next;
	}

	return false;
}


// stopwatch = start time
// countdown = start time
time_t ClockManager::getStart(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			return slider->tStart;
		}

		slider = slider->next;
	}

	return NULL;
}

// stopwatch = stop time
// countdown = finish time
time_t ClockManager::getStop(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			return slider->tStop;
		}

		slider = slider->next;
	}

	return NULL;
}

// stopwatch = stop - start
// countdown = stop - start
time_t ClockManager::getDuration(char *name) {
	Clock *slider = head;

	while(slider) {
		if(!strcmp(slider->name, name)) {
			return slider->tStop - slider->tStart;
		}

		slider = slider->next;
	}

	return NULL;
}